﻿using System;
using System.Collections.Generic;
using System.Text;
using iTextSharp.text;
using iTextSharp.text.pdf;
using SdmxMl.Common;
using SdmxMl.Structure;
using SdmxMl.Manager;


namespace SdmxMl.Pdf
{
    public class PdfVarDetail
    {
        private Rectangle pgSize;

        // Fonts used for table
        private Font fTableHeader;
        private Font fTableCell;
        private Font fTableCellBold;
        private Font fCodelistFullId;
        private RefVarHelper varHelper;
        private string lang;
        protected Document document;

        public PdfVarDetail(Document doc, string lg, Rectangle r, RefVarHelper refVar)
        {
            document = doc;
            lang = lg;
            pgSize = r;
            varHelper = refVar;

            fTableHeader = new Font(Font.FontFamily.HELVETICA, 9, Font.NORMAL, BaseColor.WHITE);
            fTableCell = new Font(Font.FontFamily.HELVETICA, 9, Font.NORMAL);
            fTableCellBold = new Font(Font.FontFamily.HELVETICA, 9, Font.BOLD);
            fCodelistFullId = new Font(Font.FontFamily.HELVETICA, 7, Font.NORMAL);
        }

        public PdfPTable PrepareListTable(string[] arrHeaderNames)
        {
            float[] colsWidth = { pgSize.Width * 0.05f, pgSize.Width * 0.20f, pgSize.Width * 0.50f, pgSize.Width * 0.25f };
            if (arrHeaderNames.Length != colsWidth.Length)
                throw new ApplicationException("Invalid Header text size received.");

            PdfPTable aTable = new PdfPTable(colsWidth.Length);
            aTable.SetWidthPercentage(colsWidth, pgSize);

            aTable.DefaultCell.Border = Rectangle.NO_BORDER;
            aTable.DefaultCell.Padding = 3;
            aTable.DefaultCell.BorderWidth = 1;
            aTable.DefaultCell.VerticalAlignment = Element.ALIGN_MIDDLE;
            

            //Creates header columns
            for (int i = 0; i < arrHeaderNames.Length; i++)
            {
                PdfPCell cell = new PdfPCell(new Phrase(arrHeaderNames[i], fTableHeader));
                cell.BackgroundColor = BaseColor.BLACK;
                aTable.AddCell(cell);
            }

            aTable.DefaultCell.BackgroundColor = BaseColor.WHITE;

            return aTable;
        }

        public PdfPTable PrepareListConceptTable(string[] arrHeaderNames)
        {
            float[] colsWidth = { pgSize.Width * 0.05f, pgSize.Width * 0.20f, pgSize.Width * 0.20f, pgSize.Width * 0.50f, pgSize.Width * 0.15f };
            if (arrHeaderNames.Length != colsWidth.Length)
                throw new ApplicationException("Invalid Header text size received.");

            PdfPTable aTable = new PdfPTable(colsWidth.Length);
            aTable.SetWidthPercentage(colsWidth, pgSize);

            aTable.DefaultCell.Border = Rectangle.NO_BORDER;
            aTable.DefaultCell.Padding = 3;
            aTable.DefaultCell.BorderWidth = 1;
            aTable.DefaultCell.VerticalAlignment = Element.ALIGN_MIDDLE;


            //Creates header columns
            for (int i = 0; i < arrHeaderNames.Length; i++)
            {
                PdfPCell cell = new PdfPCell(new Phrase(arrHeaderNames[i], fTableHeader));
                cell.BackgroundColor = BaseColor.BLACK;
                aTable.AddCell(cell);
            }

            aTable.DefaultCell.BackgroundColor = BaseColor.WHITE;

            return aTable;
        }


        public PdfPTable GenerateAlphaVarList(List<BaseDimensionType> varList)
        {
            PdfPTable aTable = PrepareListTable(new string[] { string.Empty, "Variable", "Name", "Type" });

            varList.Sort(delegate(BaseDimensionType p1, BaseDimensionType p2) 
            { return p1.ConceptInfo.Id.CompareTo(p2.ConceptInfo.Id); });

            int row = 0;
            foreach (BaseDimensionType bdt in varList)
                PrintVarSummary(row++, lang, aTable, bdt);

            return aTable;
        }

        public PdfPTable GenerateAlphaVarConcept(List<ConceptType> conceptList)
        {
            PdfPTable aTable = PrepareListConceptTable(new string[] { string.Empty, "Concept", "Parent", "Name", "Type" });

            // alpha sort
            conceptList.Sort(delegate(ConceptType p1, ConceptType p2)
            { return p1.Id.CompareTo(p2.Id); });

            int row = 0;
            foreach (ConceptType c in conceptList)
                PrintConceptSummary(row++, lang, aTable, c);

            //PutRecurseConcept(aTable, hnRoot, ref row);

            return aTable;
        }

        private void PrintVarSummary(int row, string lang, PdfPTable aTable, BaseDimensionType bdt)
        {
            if (row % 2 == 1)
                aTable.DefaultCell.GrayFill = 0.9f;

            //Numbering
            aTable.AddCell(new Phrase((row + 1).ToString(), fTableCell));

            //Var ID
            aTable.AddCell(new Phrase(bdt.ConceptInfo.Id, fTableCell));

            //Var name
            ConceptType c = varHelper.GetConcept(bdt.ConceptInfo.Id, bdt.ConceptSchemeInfo);
            aTable.AddCell(new Phrase(c.Name.GetForLanguage(lang), fTableCell));

            //TextType or codelist reference
            CodeListType clt = varHelper.GetCodelistOfBaseDim(bdt);
            if (clt != null)
                aTable.AddCell(new Phrase("Codelist", fTableCell));
            else
            {
                //Text format info instead
                TextFormatType tft = varHelper.GetTextFormatTypeOfBaseDim(bdt);

                aTable.AddCell(new Phrase(tft.TextType.ToString(), fTableCell));
            }

            if (row % 2 == 1)
                aTable.DefaultCell.GrayFill = 1.0f;
        }

        private void PrintConceptSummary(int row, string lang, PdfPTable aTable, ConceptType c)
        {
            if (row % 2 == 1)
                aTable.DefaultCell.GrayFill = 0.9f;

            //Numbering
            Phrase pn = new Phrase((row + 1).ToString(), fTableCell);
            Anchor ancNo = new Anchor();
            ancNo.Name = c.Id + "return";
            pn.Add(ancNo);

            aTable.AddCell(pn);

            //Concept ID, Parent ID
            Anchor anc = new Anchor(c.Id, fTableCell);
            anc.Reference = "#" + c.Id;
            Phrase p = new Phrase();
            p.Add(anc);

            aTable.AddCell(p);
            aTable.AddCell(new Phrase(c.Parent, fTableCell));

            //Concept name
            aTable.AddCell(new Phrase(c.Name.GetForLanguage(lang), fTableCell));

            //TextType or codelist reference
            CodeListType clt = varHelper.GetCodelist(c.CoreRepresentation);
            if (clt != null)
                aTable.AddCell(new Phrase("Codelist", fTableCell));
            else
            {
                //Text format info instead
                TextFormatType tft = c.TextFormat;
                aTable.AddCell(new Phrase(tft.TextType.ToString(), fTableCell));
            }

            if (row % 2 == 1)
                aTable.DefaultCell.GrayFill = 1.0f;
        }

        public string DetailTextFormat(TextFormatType tft)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append(tft.TextType.ToString());
            if (string.IsNullOrEmpty(tft.minLength) == false)
                sb.Append(", ").Append("MinLen: ").Append(tft.minLength);
            if (string.IsNullOrEmpty(tft.maxLength) == false)
                sb.Append(", ").Append("MaxLen: ").Append(tft.maxLength);
            if (string.IsNullOrEmpty(tft.decimals) == false)
                sb.Append(", ").Append("Decimal: ").Append(tft.decimals);

            return sb.ToString();
        }


        public PdfPTable GetDetailVarTable(int no, BaseDimensionType bdt)
        {
            PdfPTable aTable = CreateTwoColTable();
            // Header
            ++no; // numbering from 1

            ConceptType c = varHelper.GetConcept(bdt.ConceptInfo.Id, bdt.ConceptSchemeInfo);
            StringBuilder sb = new StringBuilder(80);
            sb.Append(no).Append(" - ");
            sb.Append(c.Id).Append(" \t");
            sb.Append(c.Name.GetForLanguage(lang));

            PdfPCell cell = new PdfPCell(new Phrase(sb.ToString(), fTableHeader));
            cell.BackgroundColor = BaseColor.BLACK;
            cell.Colspan = 2;
            aTable.AddCell(cell);
            
            // Description
            PutDetailTableCellRow("Description", c.Description.GetForLanguage(lang), aTable);

            // Annotations concept + base dim
            AnnotationList annList = c.AnnotationList.Clone();
            foreach (SdmxMl.Common.Annotation a in bdt.AnnotationList)
                annList.Add(a.Clone());
            PutAnnotations(annList, aTable);

            // Type 
            TextFormatType tt = varHelper.GetTextFormatTypeOfBaseDim(bdt);
            if (tt.TextType != TextTypeType.Undefined)
                PutDetailTableCellRow("Format", tt.TextType.ToString(), aTable);

            PutDetailTableCellRow(string.Empty, string.Empty, aTable);

            // If it is an attribute place attribute specialized infos
            AttributeType at = bdt as AttributeType;
            if (at != null)
            {
                PutDetailTableCellRow("Attribute Info.", string.Empty, fTableCellBold, BaseColor.LIGHT_GRAY, aTable);
                PutDetailTableCellRow("Kind", at.AttributeKind.ToString(), aTable);
                PutDetailTableCellRow("Attachement level", at.AttachmentLevel.ToString(), aTable);
                PutDetailTableCellRow("Assignement status", at.AssignmentStatus.ToString(), aTable);
                PutDetailTableCellRow(string.Empty, string.Empty, aTable);
            }


            // Codelist
            CodeListType clt = varHelper.GetCodelistOfBaseDim(bdt);
            if (clt != null)
            {
                PutDetailTableCellRow("Codelist", clt.Name.GetForLanguage(lang), fTableCellBold, BaseColor.LIGHT_GRAY, aTable);
                PutDetailTableCellRow("Identification", clt.FullIdent, fTableCellBold, BaseColor.LIGHT_GRAY, aTable);

                foreach (CodeType code in clt.CodeList)
                    PutDetailTableCellRow(code.Id, code.Name.GetForLanguage(lang), aTable);

                PutDetailTableCellRow(string.Empty, string.Empty, aTable);
            }

            return aTable;
        }

        public void PutAnnotations(AnnotationList annList, PdfPTable aTable)
        {
            annList.Sort(delegate(SdmxMl.Common.Annotation a1, SdmxMl.Common.Annotation a2)
            { return a1.Type.CompareTo(a2.Type); });

            foreach (SdmxMl.Common.Annotation a in annList)
                PutDetailTableCellRow(GetAnnTypeAndTitle(a), a.Text.GetForLanguage(lang), aTable);

        }

        public PdfPTable GetDetailConceptTable(int no, ConceptType c)
        {
            PdfPTable aTable = CreateTwoColTable();

            // Header
            ++no; // numbering from 1
            PutDetailTableCellHead(no.ToString() + " - " + c.Id + "   " + c.Name.GetForLanguage(lang), fTableHeader, aTable, c.Id);

            // Parent
            PutDetailTableCellRow("Parent", c.Parent, aTable);
            // Description
            PutDetailTableCellRow("Description", c.Description.GetForLanguage(lang), aTable);

            // Annotations 
            PutAnnotations(c.AnnotationList.Clone(), aTable);

            // Type 
            if (c.TextFormat.TextType != TextTypeType.Undefined)
                PutDetailTableCellRow("Format", DetailTextFormat(c.TextFormat), aTable);


            PutDetailTableCellRow(string.Empty, string.Empty, aTable);

            // Codelist
            CodeListType clt = varHelper.GetCodelist(c.CoreRepresentation);
            if (clt != null)
            {
                PutDetailTableCellRow("Codelist", clt.Name.GetForLanguage(lang), fTableCellBold, BaseColor.LIGHT_GRAY, aTable);
                PutDetailTableCellRow("Identification", clt.FullIdent, fTableCellBold, BaseColor.LIGHT_GRAY, aTable);

                foreach (CodeType code in clt.CodeList)
                    PutDetailTableCellRow(code.Id, code.Name.GetForLanguage(lang), aTable);

                PutDetailTableCellRow(string.Empty, string.Empty, aTable);
            }

            return aTable;
        }

        private void PutDetailTableCellRow(string sc1, string sc2, PdfPTable aTable, string targetName)
        {
            PutDetailTableCellRow(sc1, sc2, fTableCell, null, aTable, targetName);
        }

        private void PutDetailTableCellRow(string sc1, string sc2, PdfPTable aTable)
        {
            PutDetailTableCellRow(sc1, sc2, fTableCell, null, aTable, null);
        }

        private void PutDetailTableCellHead(string sc, Font f, PdfPTable aTable)
        {
            PutDetailTableCellHead(sc, f, aTable, null);
        }

        private void PutDetailTableCellHead(string sc, Font f, PdfPTable aTable, string targetId)
        {
            Phrase p = null;

            if (targetId != null)
            {
                Anchor anc = new Anchor(sc, f);
                anc.Name = targetId;
                p = new Phrase();
                p.Add(anc);
                anc = new Anchor(" ->");
                anc.Reference = "#" + targetId + "return";
                p.Add(anc);

            }
            else
                p = new Phrase(sc, f);

            PdfPCell cell = new PdfPCell(p);
            cell.Border = Rectangle.NO_BORDER;
            cell.BackgroundColor = BaseColor.BLACK;
            cell.Padding = 3;
            cell.Colspan = 2;
            aTable.AddCell(cell);

        }
        private void PutDetailTableCellRow(string sc1, string sc2, Font f, BaseColor bc, PdfPTable aTable)
        {
            PutDetailTableCellRow(sc1, sc2, f, bc, aTable, null);
        }

        private void PutDetailTableCellRow (string sc1, string sc2, Font f, BaseColor bc, PdfPTable aTable, string targetName)
        {
            PdfPCell cell = null;
            Phrase p = null;

            if (targetName != null)
            {
                Anchor anc = new Anchor(sc1, f);
                anc.Name = targetName;
                p = new Phrase();
                p.Add(anc);
            }
            else
                p = new Phrase(sc1, f);

            cell = new PdfPCell(p);
            cell.Border = Rectangle.NO_BORDER;
            if (bc != null)
                cell.BackgroundColor = bc;
            cell.Padding = 3;

            aTable.AddCell(cell);
            

            cell = new PdfPCell(new Phrase(sc2, f));
            cell.Border = Rectangle.NO_BORDER;
            if (bc != null)
                cell.BackgroundColor = bc;
            cell.Padding = 3;
            aTable.AddCell(cell);
        }
            
        


        public PdfPTable GetDetailClSchemeTable(int no, CodeListType clt)
        {
            PdfPTable aTable = CreateTwoColTable();

            // Header
            ++no; // numbering from 1
            PutDetailTableCellHead(clt.Id + " - " + clt.Name.GetForLanguage(lang), fTableHeader, aTable); 

            // Description
            PutDetailTableCellRow("Description", clt.Description.GetForLanguage(lang), aTable);

            // Annotations
            PutAnnotations(clt.AnnotationList.Clone(), aTable);

            return aTable;
        }

        public void PrintCode(int row, string lang, PdfPTable aTable, CodeType c)
        {
            if (row % 2 == 1)
                aTable.DefaultCell.GrayFill = 0.9f;

            //Numbering
            aTable.AddCell(new Phrase((row + 1).ToString(), fTableCell));

            //Code
            aTable.AddCell(new Phrase(c.Id, fTableCell));

            //code name

            aTable.AddCell(new Phrase(c.Name.GetForLanguage(lang), fTableCell));

            //code descr
            string descr = c.Description.IsEmpty ? string.Empty : c.Description.GetForLanguage(lang);
            aTable.AddCell(new Phrase(descr, fTableCell));

            if (row % 2 == 1)
                aTable.DefaultCell.GrayFill = 1.0f;
        }

        public PdfPTable CreateTwoColTable()
        {
            float[] colsWidth = { pgSize.Width * 0.25f, pgSize.Width * 0.75f };

            PdfPTable aTable = new PdfPTable(colsWidth.Length);
            aTable.SetWidthPercentage(colsWidth, pgSize);

            aTable.DefaultCell.Border = Rectangle.NO_BORDER;
            aTable.DefaultCell.Padding = 3;
            aTable.DefaultCell.BorderWidth = 1;
            //aTable.DefaultCell.VerticalAlignment = Element.ALIGN_MIDDLE;

            aTable.DefaultCell.BackgroundColor = BaseColor.WHITE;
            return aTable;
        }


    

        public PdfPTable GetDetailArtefactTable(Artefact a)
        {
            PdfPTable aTable = CreateTwoColTable();
            // Header
            PutDetailTableCellHead(a.Id + "   " + a.Name.GetForLanguage(lang), fTableHeader, aTable);

            // Description
            PutDetailTableCellRow("Description", a.Description.GetForLanguage(lang), aTable);

            // Validity
            if (string.IsNullOrEmpty(a.ValidFrom) == false)
                PutDetailTableCellRow("Valid From", a.ValidFrom, aTable);
            if (string.IsNullOrEmpty(a.ValidTo) == false)
                PutDetailTableCellRow("Valid To", a.ValidTo, aTable);

            PutDetailTableCellRow("Version", a.Version, aTable);

            if (string.IsNullOrEmpty(a.Uri) == false)
                PutDetailTableCellRow("URI", a.Uri, aTable);
            if (string.IsNullOrEmpty(a.Urn) == false)
                PutDetailTableCellRow("URN", a.Uri, aTable);

            // Annotations
            PutAnnotations(a.AnnotationList.Clone(), aTable);

            return aTable;
        }

        public void GetDetailMsdTarget(Document doc, TargetIdentifiersType targetId)
        {
            doc.Add(new Paragraph("Target Identifiers"));
            doc.Add(new Paragraph(" "));
            PdfPTable aTable = CreateTwoColTable();
            // Header
            PutDetailTableCellHead(targetId.FullTargetIdentifier.Id + "   " + targetId.FullTargetIdentifier.Name.GetForLanguage(lang), fTableHeader, aTable);

            // Description
            PutDetailTableCellRow("Description", targetId.FullTargetIdentifier.Description.GetForLanguage(lang), aTable);

            if (string.IsNullOrEmpty(targetId.FullTargetIdentifier.Uri) == false)
                PutDetailTableCellRow("URI", targetId.FullTargetIdentifier.Uri, aTable);
            if (string.IsNullOrEmpty(targetId.FullTargetIdentifier.Urn) == false)
                PutDetailTableCellRow("URN", targetId.FullTargetIdentifier.Urn, aTable);

            // Annotations
            PutAnnotations(targetId.FullTargetIdentifier.AnnotationList.Clone(), aTable);

            doc.Add(aTable);
            doc.Add(new Paragraph("Identifier Components"));
            doc.Add(new Paragraph(" "));

            int row = 0;
            foreach( IdentifierComponentType idt in targetId.FullTargetIdentifier.IdentifierComponentList)
            {
                doc.Add(GetDetailTargetTable(row, idt));
                ++row;
            }
        }


        public void GetDetailMsdReport(Document doc, ReportStructureType report)
        {
            doc.NewPage();
            doc.Add(new Paragraph("Report: " + report.Id));
            doc.Add(new Paragraph(" "));
            PdfPTable aTable = CreateTwoColTable();
            // Header
            PutDetailTableCellHead(report.Id + "   " + report.Name.GetForLanguage(lang), fTableHeader, aTable);

            // Target
            PutDetailTableCellRow("Target Reference", report.TargetId, aTable);
            // Description
            PutDetailTableCellRow("Description", report.Description.GetForLanguage(lang), aTable);

            //URI / URN
            if (string.IsNullOrEmpty(report.Uri) == false)
                PutDetailTableCellRow("URI", report.Uri, aTable);
            if (string.IsNullOrEmpty(report.Urn) == false)
                PutDetailTableCellRow("URN", report.Urn, aTable);

            // Annotations
            PutAnnotations(report.AnnotationList.Clone(), aTable);

            doc.Add(aTable);
            doc.Add(new Paragraph("Meta Attributes of report:"));
            doc.Add(new Paragraph(" "));

            int row = 0;
            RecurseMetaAttrib(doc, null, report.MetadataAttributeList, ref row);
        }

        private void RecurseMetaAttrib(Document doc, MetadataAttributeType parent, MetadataAttributeTypeList metaAttList, ref int row)
        {
            foreach (MetadataAttributeType metaAtt in metaAttList)
            {

                doc.Add(GetDetailMetaAttributeTable(row, parent, metaAtt));
                ++row;
                RecurseMetaAttrib(doc, metaAtt, metaAtt.MetadataAttributeList, ref row);
            }
        }

        public PdfPTable GetDetailTargetTable(int no, IdentifierComponentType idt)
        {
            PdfPTable aTable = CreateTwoColTable();

            // Header
            ++no; // numbering from 1
            PutDetailTableCellHead(idt.Id + " - " + idt.Name.GetForLanguage(lang), fTableHeader, aTable);

            // TOB
            PutDetailTableCellRow("Target Ob. Class", idt.TargetObjectClass.ToString(), aTable);

            // Description
            PutDetailTableCellRow("Description", idt.Description.GetForLanguage(lang), aTable);

            if (string.IsNullOrEmpty(idt.Uri) == false)
                PutDetailTableCellRow("URI", idt.Uri, aTable);
            if (string.IsNullOrEmpty(idt.Urn) == false)
                PutDetailTableCellRow("URN", idt.Urn, aTable);
            // Annotations
            PutAnnotations(idt.AnnotationList.Clone(), aTable);

            return aTable;
        }

        public PdfPTable GetDetailMetaAttributeTable(int no, MetadataAttributeType parent, MetadataAttributeType metaAtt)
        {
            PdfPTable aTable = CreateTwoColTable();

            // Header
            ++no; // numbering from 1

            ConceptType c = varHelper.GetConcept(metaAtt.ConceptInfo.Id, metaAtt.ConceptSchemeInfo);
            StringBuilder sb = new StringBuilder(80);
            sb.Append(no).Append(" - ");
            sb.Append(c.Id).Append(" \t");
            sb.Append(c.Name.GetForLanguage(lang));

            PdfPCell cell = new PdfPCell(new Phrase(sb.ToString(), fTableHeader));
            cell.BackgroundColor = BaseColor.BLACK;
            cell.Colspan = 2;
            aTable.AddCell(cell);

            // Parent
            if( parent != null)
                PutDetailTableCellRow("Parent Attribute", parent.ConceptInfo.Id, aTable);

            // Description
            PutDetailTableCellRow("Description", c.Description.GetForLanguage(lang), aTable);

            // Usage
            PutDetailTableCellRow("Usage", metaAtt.UsageStatus.ToString(), aTable);

            // Annotations concept + metaatt
            AnnotationList annList = c.AnnotationList.Clone();
            foreach (SdmxMl.Common.Annotation a in metaAtt.AnnotationList)
                annList.Add(a.Clone());
            PutAnnotations(annList, aTable);

            // Type 
            TextFormatType tt = varHelper.GetTextFormatTypeOfBaseDim(metaAtt);
            if (tt.TextType != TextTypeType.Undefined)
                PutDetailTableCellRow("Format", tt.TextType.ToString(), aTable);

            PutDetailTableCellRow(string.Empty, string.Empty, aTable);

            // Codelist
            CodeListType clt = varHelper.GetCodelistOfMetaAtt(metaAtt);
            if (clt != null)
            {
                PutDetailTableCellRow("Codelist", clt.Name.GetForLanguage(lang), fTableCellBold, BaseColor.LIGHT_GRAY, aTable);
                PutDetailTableCellRow("Identification", clt.FullIdent, fTableCellBold, BaseColor.LIGHT_GRAY, aTable);

                foreach (CodeType code in clt.CodeList)
                    PutDetailTableCellRow(code.Id, code.Name.GetForLanguage(lang), aTable);

                PutDetailTableCellRow(string.Empty, string.Empty, aTable);
            }

            return aTable;
        }

        private string GetAnnTypeAndTitle(SdmxMl.Common.Annotation a)
        {
            string s = a.Type;
            if (string.IsNullOrEmpty(a.Title) == false)
                s += " " + a.Title;
            return s;
        }

    }
}
